
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_shd.h"

/* If you declare any globals in php_shd.h uncomment this:
ZEND_DECLARE_MODULE_GLOBALS(shd)
*/

/* True global resources - no need for thread safety here */
static int le_shd;

zend_class_entry *i_class_ce, *parent_class_ce, *class_ce;

/* {{{ shd_functions[]
 *
 * Every user visible function must have an entry in shd_functions[].
 */
/*
ZEND_BEGIN_ARG_INFO(byref_compiletime_arginfo, 0)
	ZEND_ARG_PASS_INFO(1)
	ZEND_ARG_PASS_INFO(0)
ZEND_END_ARG_INFO()
*/
ZEND_BEGIN_ARG_INFO(shd_params_arginfo, 0)
	//ZEND_ARG_ARRAY_INFO(0, arr, 0)
	ZEND_ARG_INFO(0, param1)
	ZEND_ARG_INFO(0, param2)
ZEND_END_ARG_INFO()

const zend_function_entry shd_functions[] = {
	PHP_FE(shd_params,	shd_params_arginfo)		/* For testing, remove later. */
	PHP_FE(shd_hashtable, NULL)
	PHP_FE(shd_foreach, NULL)
	PHP_FE(shd_array, NULL)
	PHP_FE_END	/* Must be the last line in shd_functions[] */
};

const zend_function_entry shd_i_myinterface_functions[] = {
	ZEND_ABSTRACT_ME(i_myinterface, public_method, NULL)
	PHP_FE_END
};

const zend_function_entry shd_parent_myclass_functions[] = {
	ZEND_ME(parent_myclass, public_method, NULL, ZEND_ACC_PUBLIC)
	PHP_FE_END
};

const zend_function_entry shd_myclass_functions[] = {
	ZEND_ME(myclass, public_method, NULL, ZEND_ACC_PUBLIC)
	PHP_FE_END
};
/* }}} */

/* {{{ shd_module_entry
 */
zend_module_entry shd_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
	STANDARD_MODULE_HEADER,
#endif
	"shd",
	shd_functions,
	PHP_MINIT(shd),
	NULL,
	NULL,		/* Replace with NULL if there's nothing to do at request start */
	NULL,	/* Replace with NULL if there's nothing to do at request end */
	PHP_MINFO(shd),
#if ZEND_MODULE_API_NO >= 20010901
	"0.1", /* Replace with version number for your extension */
#endif
	STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_SHD
ZEND_GET_MODULE(shd)
#endif

PHP_MINIT_FUNCTION(shd) {
	zend_class_entry ce, p_ce, i_ce;

	// interface
	INIT_CLASS_ENTRY(i_ce, "i_myinterface", shd_i_myinterface_functions);
	i_class_ce = zend_register_internal_interface(&i_ce TSRMLS_CC);

	// parent class
	INIT_CLASS_ENTRY(p_ce, "parent_myclass", shd_parent_myclass_functions);
	parent_class_ce = zend_register_internal_class(&p_ce TSRMLS_CC);
	zend_class_implements(parent_class_ce TSRMLS_CC, 1, i_class_ce);

	// child class
	INIT_CLASS_ENTRY(ce, "myclass", shd_myclass_functions);
	//class_ce = zend_register_internal_class(&ce TSRMLS_CC);
	class_ce = zend_register_internal_class_ex(&ce, parent_class_ce, "parent_myclass" TSRMLS_CC);

	zend_declare_property_null(class_ce, "public_var", sizeof("public_var"), ZEND_ACC_PUBLIC);

	return SUCCESS;
}

PHP_MINFO_FUNCTION(shd)
{
	php_info_print_table_start();
	php_info_print_table_header(2, "shd support", "enabled");
	php_info_print_table_end();

	/* Remove comments if you have entries in php.ini
	DISPLAY_INI_ENTRIES();
	*/
}
/* }}} */

// test params
ZEND_FUNCTION(shd_params) {
	zval *val, *val2;
	int count = 0;
	MAKE_STD_ZVAL(val2);
	ZVAL_STRING(val2, "test", 1);
	char *ret;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z", &val, &val2)
		== FAILURE) {
		RETURN_NULL();
	}

	ZVAL_STRING(val, "modified by internal", 1);
	//sprintf(ret, "%s, %s", Z_STRVAL_P(val), Z_STRVAL_P(val2));
	asprintf(&ret, "(%s, %s)", Z_STRVAL_P(val), Z_STRVAL_P(val2));
	RETURN_STRING(ret, 1);
	//PHPWRITE(ret, sizeof(ret)-1);
}

// test hashtable
ZEND_FUNCTION(shd_hashtable) {
	zval *val = NULL;
	zval *val2 = NULL;
	zval *val3 = NULL;
    zval *ht_arr = NULL;

	MAKE_STD_ZVAL(ht_arr);
	MAKE_STD_ZVAL(val);
	MAKE_STD_ZVAL(val2);
	MAKE_STD_ZVAL(val3);

	array_init(ht_arr);

	ZVAL_STRING(val,"dopool",1);
	ZVAL_STRING(val2,"value two",1);
	ZVAL_STRING(val3,"value three",1);

	//zend_hash_init(&ht, 64, NULL, ZVAL_PTR_DTOR, 0);
	zend_hash_add(Z_ARRVAL_P(ht_arr), "a", sizeof("a"), &val, sizeof(zval*), NULL);
	zend_hash_add(Z_ARRVAL_P(ht_arr), "b", sizeof("b"), &val2, sizeof(zval*), NULL);
	zend_hash_update(Z_ARRVAL_P(ht_arr), "a", sizeof("a"), &val3, sizeof(zval*), NULL);

	PHPWRITE(val, 1);

	RETVAL_ZVAL(ht_arr, 1, 0);
}

// array foreach
ZEND_FUNCTION(shd_foreach) {
	zval *val;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &val)
		== FAILURE) {
		RETURN_FALSE;
	}
	if (Z_TYPE_P(val) != IS_ARRAY) {
		RETURN_FALSE;
	}

	zend_hash_apply(Z_ARRVAL_P(val), php_sample_print_zval);

	RETURN_TRUE;
}

// foreach
int php_sample_print_zval(zval **val)
{
	ZVAL_STRING(*val, "modified by foreach()", 1);
    //返回，继续遍历下一个～
    return ZEND_HASH_APPLY_KEEP;
}

ZEND_FUNCTION(shd_array) {
	zval *val = NULL, *subval = NULL;
	MAKE_STD_ZVAL(val);
	MAKE_STD_ZVAL(subval);
	array_init(val);
	array_init(subval);

	add_assoc_long(val, "test_key", 1111);
	add_index_long(val, 1, 1111);
	add_next_index_long(val, 1111);

	// add sub array
	add_next_index_string(subval, "A String", 1);
	add_next_index_zval(val, subval);
	add_assoc_zval(val, "test_key", subval);

	RETURN_ZVAL(val, 1, 0);
}

ZEND_METHOD(myclass, public_method) {
	php_printf("I am public method.");
}

ZEND_METHOD(parent_myclass, public_method) {
	php_printf("I am parent public method.");
}

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
